<template>
  <div class="tool-bar">
    <px-row>
      <!-- <px-button>JSON</px-button> -->
      <px-button @click="backMainPage">返回</px-button>
      <px-button @click="undo">撤销</px-button>
      <px-button @click="redo">重做</px-button>
      <px-button @click="preview">预览</px-button>
      <px-button @click="onExportJSON">导出</px-button>
      <label for="input" class="insert">
        插入图片
        <input id="input" type="file" hidden @change="handleFileChange" />
      </label>
      <px-button :disabled="!areaData.components.length" @click="compose">组合</px-button>
      <px-button @click="isShowComposeDialog = true">新增分组</px-button>

      <px-button :disabled="!curComponent || curComponent.isLock || curComponent.component != 'Group'" @click="decompose"> 拆分 </px-button>
      <px-button :disabled="!curComponent || curComponent.isLock" @click="lock">锁定</px-button>
      <px-button :disabled="!curComponent || !curComponent.isLock" @click="unlock">解锁</px-button>
      <px-button @click="save">保存</px-button>
      <px-button @click="saveSyncGroupStyle">更新分组样式</px-button>

      <px-button @click="viewData">数据编辑</px-button>

      <div class="canvas-config">
        <span>画布大小</span>
        <input v-model="canvasStyleData.width" />
        <span>*</span>
        <input v-model="canvasStyleData.height" />
      </div>
      <div class="canvas-config">
        <span>画布比例</span>
        <px-input-number v-model="scale" :min="1" :max="200" controls-position="right" size="small" :step="10" @change="handleScaleChange" />%
      </div>

      <px-button class="canvas-config" @click="clearCanvas">清空画布</px-button>
      <px-button class="canvas-config" @click="batchPutDot">批量打点</px-button>
    </px-row>
    <!-- 预览 -->
    <Preview v-if="isShowPreview" :is-screenshot="isScreenshot" @close="handlePreviewChange" />

    <px-dialog
      v-model="isShowDialog"
      :title="isExport ? '导出数据' : '导入数据'"
      :close-on-press-escape="false"
      :close-on-click-modal="false"
      width="600"
    >
      <px-input v-model="jsonData" type="textarea" :rows="20" placeholder="请输入 JSON 数据" />
      <template v-slot:footer>
        <div class="dialog-footer">
          <px-button @click="isShowDialog = false">取 消</px-button>

          <px-button type="primary" @click="processJSON">确 定</px-button>
        </div>
      </template>
    </px-dialog>
    <px-dialog v-model="isShowComposeDialog" title="新建分组" :close-on-press-escape="false" :close-on-click-modal="false" width="600">
      <px-form>
        <px-form-item label="设置分组名称">
          <px-input v-model="compLabel" />
        </px-form-item>
        <px-form-item label="选择分组组件">
          <px-select v-model="compSelected" value-key="id" placeholder="Select" multiple collapse-tags collapse-tags-tooltip>
            <px-option v-for="(item, index) in componentData" :key="index" :label="getComponent(index).label" :value="getComponent(index)" />
          </px-select>
        </px-form-item>
      </px-form>

      <template v-slot:footer>
        <div class="dialog-footer">
          <px-button @click="composeDialogClose">取 消</px-button>

          <px-button type="primary" @click="composeDialogConfirm">确 定</px-button>
        </div>
      </template>
    </px-dialog>
    <px-dialog v-model="isShowJsonDialog" title="查看及编辑配置数据" :close-on-press-escape="false" :close-on-click-modal="false" width="600">
      <AceEditor ref="aceRef" :config-json="componentData" />
      <template v-slot:footer>
        <px-button @click="saveEditerData">修改</px-button>
        <px-button @click="isShowJsonDialog = false">关闭</px-button>
      </template>
    </px-dialog>
    <px-dialog
      v-model="isShowPutDotDialog"
      title="批量设置打点数据"
      width="70%"
      :close-on-press-escape="false"
      :close-on-click-modal="false"
      @close="batchReset"
    >
      <px-form :model="putDotForm" label-width="120px" style="max-width: 400px">
        <px-form-item label="选择组件">
          <px-select v-model="putDotForm.selectComp" placeholder="选择组件" value-key="name" @change="selectCompChange">
            <px-option v-for="(item, index) in dotComps" :key="index" :label="item.name" :value="item" />
          </px-select>
        </px-form-item>
        <px-form-item label="选择数据源">
          <px-col :span="15">
            <px-select v-model="putDotForm.selectSource" value-key="id" placeholder="选择数据源" @change="selectSourceChange">
              <px-option v-for="item in dotSourceOpt" :key="item.id" :label="item.label" :value="item" />
            </px-select>
          </px-col>
          <px-col :span="5" offset="'1'">
            <px-button type="primary" @click="updateTree()">刷新数据</px-button>
          </px-col>
        </px-form-item>
        <px-form-item label="数据访问路径">
          <px-col :span="15">
            <px-form-item>
              <px-input v-model="putDotForm.dataPath" placeholder="请输入数据路径" />
            </px-form-item>
          </px-col>
          <px-col :span="15">
            <px-form-item>
              <px-input v-model="putDotForm.dataNum" placeholder="请输入筛选数量" />
            </px-form-item>
          </px-col>
          <px-col :span="5">
            <px-form-item>
              <px-button type="primary" @click="sourceFilter">筛选数据</px-button>
            </px-form-item>
          </px-col>
        </px-form-item>
        <px-form-item label="过滤已经存在数据">
          <px-switch v-model="isFilter" size="small" />
        </px-form-item>
        <px-form-item label="点位数量">
          {{ bindingData ? bindingData.length : 0 }}
        </px-form-item>
        <px-form-item label="选择打点">
          <px-select v-model="dotSelected" filterable multiple placeholder="Select" style="width: 240px" value-key="id" all-option>
            <px-option v-for="item in dotSelectData" :key="item.id" :label="item.label" :value="item" />
          </px-select>
        </px-form-item>
      </px-form>
      <div class="w-[100%] flex justify-between">
        <div class="w-[45%]">
          <h3 class="text-[#000c] mb-[10px] font-bold">数据源</h3>
          <AceEditor ref="bindingAceRef" :config-json="bindingData" />
        </div>
        <div class="w-[45%]">
          <h3 class="text-[#000c] mb-[10px] font-bold">字段映射</h3>
          <AceEditor ref="aliaseAceRef" :config-json="aliaseData" />
        </div>
      </div>
      <template v-slot:footer>
        <div class="dialog-footer">
          <px-button @click="isShowPutDotDialog = false">取 消</px-button>
          <px-button type="primary" @click="batchPutDotCreate('group')">合并分组生成</px-button>

          <px-button type="primary" @click="batchPutDotCreate()">生 成</px-button>
        </div>
      </template>
    </px-dialog>
  </div>
</template>
<script setup lang="ts">
import { useComponent, useSnapshot, useCompose, useContextMenu } from "@visual/stores";
import { inject, reactive, ref, toRefs, watch } from "vue";
import Preview from "./Editor/Preview.vue";
import { PxMessage } from "px-ui";
import { storeToRefs } from "pinia";
import {
  eventBus,
  $,
  deepcopy,
  getValueByPathAndNum,
  mergePropValue,
  compressArray,
  changeComponentSizeWithScale,
  changeComponentsSizeWithScale,
  createGroupStyle,
  getComponentRotatedStyle
} from "@visual/utils";
import { nanoid } from "nanoid";
import { commonStyle, commonAttr } from "@visual/custom-component/component-list";
import AceEditor from "@visual/components/Editor/AceEditor.vue";
import componentList from "@visual/custom-component/component-list";
import { queryMonitor, queryTempAndHumid } from "@/api/visual/visualConfig";
import { cloneDeep } from "@pureadmin/utils";
const facCode = localStorage.getItem("facCode");
const { undo, redo } = useSnapshot();
const curTreeData = inject("curTreeData", ref({}));
const curFacCode = inject("positionCode", ref(""));
const isShowPreview = ref(false);
const isScreenshot = ref(false);
const jsonData = ref("");
const isExport = ref(false);
const isShowDialog = ref(false);
const isShowJsonDialog = ref(false);
const dotSelected = ref();
const dotSelectData = ref([]);
/**
 * 批量打点相关参数
 * isShowPutDotDialog 弹窗显示
 * putDotForm 表单信息，未来需要添加必填校验
 * selectCompConfig 选择的组件
 * dotComps 组件选项
 * aliaseAceRef 字段映射Ace编辑器
 * dotSourceOpt 数据源选项
 *
 */
const isShowPutDotDialog = ref(false);
const putDotForm = reactive({ selectComp: null, dataPath: "value", dataNum: "", selectSource: null, aliaseData: null, bindingData: null });
const { selectComp, dataPath, selectSource, aliaseData, bindingData, dataNum } = toRefs(putDotForm);
let selectCompConfig = null;
const dotComps = ref([
  { name: "下钻点位", value: "DrillLocation" },
  { name: "跳转点位", value: "TextLocation" },
  {
    name: "摄像头",
    value: "RectLocation",
    type: "camera"
  },

  { name: "传感器点位", value: "RectLocation", type: "sensor" },
  { name: "温湿度点位", value: "RectLocation", type: "temperature" },

  { name: "报警点位", value: "ColorLocation", type: "error" }
]);
const aliaseAceRef = ref();
const dotSourceOpt = ref([]);

const componentStore = useComponent();
const { setCurComponent, setComponentData, addComponent, setEditMode, setCurComponentLockStatus } = componentStore;

const { componentData, canvasStyleData, curComponent } = storeToRefs(componentStore);
const emits = defineEmits(["backMainPage", "saveTemplData"]);
const aceRef = ref();
const bindingAceRef = ref();
const props = defineProps({
  visualData: {
    type: Array,
    default: () => []
  }
});
const isShowComposeDialog = ref(false);
const isFilter = ref(false);
// 放大缩小
const scale = ref(100);
let timer = null;
function handleScaleChange() {
  clearTimeout(timer);

  timer = setTimeout(() => {
    scale.value = ~~scale.value || 1;
    changeComponentsSizeWithScale(scale.value);
    canvasStyleData.value.scale = scale.value;
  }, 1000);
}

function backMainPage() {
  emits("backMainPage");
}
function updateTree() {
  eventBus.emit("refreshBaseTree");
}
watch(
  () => curTreeData.value,
  async val => {
    if (val) {
      if (isShowPutDotDialog.value) {
        batchReset();
        await batchPutDot();
      }
    }
  }
);
function viewData() {
  isShowJsonDialog.value = true;
}
function preview() {
  isScreenshot.value = false;
  isShowPreview.value = true;
  setEditMode("preview");
}

function handlePreviewChange() {
  isShowPreview.value = false;
  setEditMode("edit");
}
function lock() {
  if (!curComponent) return;
  setCurComponentLockStatus(true);
}
function unlock() {
  if (!curComponent) return;
  setCurComponentLockStatus(false);
}

function save() {
  // localStorage.setItem("canvasData", JSON.stringify(componentData.value));
  // localStorage.setItem("canvasStyle", JSON.stringify(canvasStyleData.value));
  // PxMessage.success("保存成功");
  // syncGroupStyle(componentData);
  emits("saveTemplData");
}
function saveSyncGroupStyle() {
  syncGroupStyle(componentData);
}
const { recordSnapshot } = useSnapshot();
function clearCanvas() {
  setCurComponent({ component: null, index: null });
  setComponentData([]);
  recordSnapshot();
}

/**
 * 批量打点相关函数，未来需要优化为组件
 * selectSourceChange
 * batchPutDot 打开弹窗并初始化数据源选项
 * sourceFilter 数据源筛选
 * selectCompChange 组件选项change时刷字段映射Json编辑器
 * batchPutDotCreate 批量生成打点数据
 *
 */
function selectSourceChange() {
  bindingData.value = deepcopy(selectSource.value);
  let template = getValueByPathAndNum(bindingData.value, "value");
  template = filterArrayByUniqueKey(template, "groupName");
  if (template && template.length > 0) {
    template = template.map(item => {
      const { name, code, eqpName, groupName } = item;
      let label = "";
      if (name) {
        label = `${code}/${name}`;
      } else {
        label = eqpName;
      }
      if (groupName) label = groupName;
      return {
        label: label,
        ...item
      };
    });
  }
  dotSelected.value = null;
  dotSelectData.value = template;
}
async function batchPutDot() {
  //来源于树上的数据
  if (dotSourceOpt.value.length === 0) {
    let drillCompsRaw = cloneDeep(componentData.value);
    let drillComps = [];
    let routerKeys = [];
    let groupNames = [];
    let cameraUrls = [];
    if (isFilter.value) {
      drillCompsRaw.forEach(item => {
        let pointStr = "DrillLocation TextLocation ColorLocation RectLocation VLocation DrillLocation2 MessageLocation";
        if (pointStr.includes(item.component)) {
          drillComps.push(item);
          if (item.propValue?.routerKey) {
            routerKeys.push(item.propValue?.routerKey);
          } else if (item.propValue?.code) {
            routerKeys.push(item.propValue?.code);
          }
          if (item.propValue?.groupName) {
            groupNames.push(item.propValue?.groupName);
          }
          if (item.propValue?.cameraUrl) {
            cameraUrls.push(item.propValue?.cameraUrl);
          }
        } else if (item.component === "Group") {
          item?.propValue?.components?.forEach(item => {
            if (pointStr.includes(item.component)) {
              drillComps.push(item);
              if (item.propValue?.routerKey) {
                routerKeys.push(item.propValue?.routerKey);
              } else if (item.propValue?.code) {
                routerKeys.push(item.propValue?.code);
              }
              if (item.propValue?.groupName) {
                groupNames.push(item.propValue?.groupName);
              }
              if (item.propValue?.cameraUrl) {
                cameraUrls.push(item.propValue?.cameraUrl);
              }
            }
          });
        }
      });
    }

    let mapSource = [];
    let source = cloneDeep(curTreeData.value) as any;

    if (source) {
      if (source[curFacCode.value]) source = source[curFacCode.value];
    }

    if (source) {
      source?.children?.forEach(item => {
        if (item.children) delete item.children;
      });
      if (isFilter.value && source?.children) {
        source.children = source?.children.filter(item => {
          if (item.groupName) return !routerKeys.includes(item.code) && !groupNames.includes(item.groupName);
          return !routerKeys.includes(item.code);
        });
      }
      const divGroup = groupBy(source?.children, "eqpType");
      if (Object.keys(divGroup)?.length > 1 || divGroup["ERC"]) {
        for (let key in divGroup) {
          mapSource.push({ value: divGroup[key], label: key, id: key });
        }
      } else {
        mapSource.push({ value: source?.children, label: source.name, id: source.code });
      }
    }
    //摄像头
    let monitorSource = await queryMonitor({ positionCode: curFacCode.value });
    if (monitorSource?.length > 0) {
      if (isFilter.value) {
        monitorSource = monitorSource.filter(item => !cameraUrls.includes(item.rtspUrl));
      }
      if (monitorSource?.length > 0) {
        mapSource.push({ value: monitorSource, label: "摄像头", id: "monitorSource" });
      }
    }
    let tempAndHumidSource = await queryTempAndHumid({ facCode: facCode, positionCode: curFacCode.value });
    tempAndHumidSource = Object.keys(tempAndHumidSource).map(key => {
      const val = tempAndHumidSource[key];
      let res = {};
      res["groupName"] = key;

      if (val) {
        val.forEach(item => {
          const { type } = item;
          for (let i in item) {
            if (i == "type") continue;
            res[mergeToCamelCase(type, i)] = item[i];
          }
        });
      }
      return res;
    });
    if (tempAndHumidSource && tempAndHumidSource.length > 0) {
      mapSource.push({ value: tempAndHumidSource, label: "温湿度", id: "tempAndHumidSource" });
    }
    dotSourceOpt.value = mapSource;
  }
  isShowPutDotDialog.value = true;
}
watch(isFilter, async val => {
  batchReset();
  await batchPutDot();
});
function groupBy(arr, key) {
  // 创建一个空对象用来存储分组结果
  const result = {};
  if (!arr) return {};
  // 遍历数组
  arr.forEach(item => {
    // 获取当前对象中指定键的值
    const keyValue = item[key];

    // 如果结果对象中还没有这个键，则初始化一个空数组
    if (!result[keyValue]) {
      result[keyValue] = [];
    }

    // 将当前对象放入对应键的数组中
    result[keyValue].push(item);
  });

  return result;
}
function sourceFilter() {
  let res = getValueByPathAndNum(selectSource.value, dataPath.value);
  if (dataNum.value) {
    res = filterData(res, dataNum.value);
  }
  bindingData.value = deepcopy(res || {});
}
function selectCompChange() {
  selectCompConfig = componentList.find(item => item.component === selectComp.value.value);
  if (selectComp.value.type) {
    const { type } = selectComp.value;
    selectCompConfig.propValue.type = type;
    selectCompConfig.propValue.filter = `menu_all menu_${type}`;
  }
  aliaseData.value = [];
  let dataKeys = Object.keys(selectCompConfig.propValue);
  if (selectCompConfig.dataAliase && JSON.stringify(selectCompConfig.dataAliase) !== "{}") {
    for (let key in selectCompConfig.dataAliase) {
      aliaseData.value.push({
        raw: key,
        alias: selectCompConfig.dataAliase[key]
      });
    }
  } else {
    aliaseData.value = dataKeys.map(item => {
      return {
        raw: item,
        alias: ""
      };
    });
  }
}
function batchPutDotCreate(flag = null) {
  //差一个数据融合，即把bindingData.value数据通过映射数组aliaseData.value赋值给selectCompConfig，
  const aliaseMap = compressArray(aliaseAceRef.value.getValue());
  let cnt = 1;
  let leftCnt = 0;
  bindingData.value = bindingAceRef.value.getValue();
  let dealData = !flag ? bindingData.value : filterArrayByUniqueKey(bindingData.value, "groupName");
  if (dotSelected.value && dotSelected.value.length > 0) {
    dealData = dotSelected.value;
  }
  dealData?.forEach((item, index) => {
    const selected = deepcopy(selectCompConfig);
    selected.id = nanoid();
    if (leftCnt * 160 > 1000) {
      cnt += 1;
      leftCnt = 0;
    }
    selected.style.top = cnt * 70;

    selected.style.left = ++leftCnt * 160;

    if (selectComp.value.type) mergePropValue(item, selected.propValue, aliaseMap, ["type"]);
    else mergePropValue(item, selected.propValue, aliaseMap);
    addComponent({ component: selected });
  });
  PxMessage({
    message: "生成成功",
    type: "success"
  });
  isShowPutDotDialog.value = false;
  batchReset();
}
function filterData(data, input) {
  // 如果数据源为空或null，返回空数组
  if (!Array.isArray(data) || data.length === 0) {
    return [];
  }

  // 解析输入的字符串
  const ranges = input.split(",").map(range => range.trim());
  let result = [];

  // 处理每一个范围
  ranges.forEach(range => {
    const parts = range.split("-").map(Number);

    if (parts.some(isNaN)) {
      console.error(`Invalid range: "${range}". All values must be numbers.`);
      return;
    }

    // 如果是单个数字，处理为取对应索引的数据
    if (parts.length === 1) {
      const index = parts[0] - 1; // 转换为0基索引
      if (index >= 0 && index < data.length) {
        result.push(data[index]);
      } else {
        console.warn(`Index ${index + 1} out of bounds.`);
      }
    } else if (parts.length === 2) {
      // 如果是范围，则处理为取对应索引范围的数据
      let start = parts[0] - 1; // 转换为0基索引
      let end = parts[1] - 1;

      // 如果范围超出了数据的最大索引，自动调整end为data.length - 1
      if (start < 0) start = 0;
      if (end >= data.length) end = data.length - 1;

      // 处理无效范围
      if (start > end) {
        console.warn(`Invalid range: "${range}". Skipping.`);
      } else {
        result = result.concat(data.slice(start, end + 1));
      }
    }
  });

  return result;
}
function filterArrayByUniqueKey(arr, key) {
  if (!arr) return [];
  const seen = new Set();
  const result = [];

  for (const item of arr) {
    if (key === null || item[key] === null || typeof item[key] == "undefined") {
      // 如果key为null或者对象中key的值为null，则保留该对象
      result.push(item);
    } else if (!seen.has(item[key])) {
      // 如果key的值尚未出现过，则保留该对象，并将其值加入到seen集合中
      seen.add(item[key]);
      result.push(item);
    }
  }

  return result;
}
function batchReset() {
  dotSourceOpt.value = [];
  dotSelected.value = null;
  Object.assign(putDotForm, { selectComp: null, selectSource: null, aliaseData: null, bindingData: null });
}
function flattenDataByKeys(data, keys) {
  const keyArray = keys.split(" ");

  function recursiveFlatten(obj, result = {}) {
    if (Array.isArray(obj)) {
      obj.forEach(item => recursiveFlatten(item, result));
    } else if (typeof obj === "object" && obj !== null) {
      let hasKey = keyArray.some(key => obj.hasOwnProperty(key));
      let resultKey = keyArray
        .map(key => obj[key])
        .filter(Boolean)
        .join("_");

      if (hasKey && resultKey) {
        result[resultKey] = obj;
      }

      for (let key in obj) {
        recursiveFlatten(obj[key], result);
      }
    }
    return result;
  }

  return recursiveFlatten(data);
}
function onExportJSON() {
  // const data = JSON.stringify(componentData.value);
  // const blob = new Blob([data], { type: "text/plain" });
  // const url = URL.createObjectURL(blob);
  // const a = document.createElement("a");
  // a.download = "data.json";
  // a.href = url;
  // a.click();
  // URL.revokeObjectURL(url);
  isShowDialog.value = true;
  isExport.value = true;
  jsonData.value = JSON.stringify(componentData.value, null, 4);
}
function processJSON() {
  try {
    const data = JSON.parse(jsonData.value);
    if (!Array.isArray(data)) {
      PxMessage.error("数据格式错误，组件数据必须是一个数组");
      return;
    }

    if (isExport.value) {
      downloadFileUtil(jsonData.value, "application/json", "data.json");
    } else {
      setComponentData(data);
    }

    isShowDialog.value = false;
  } catch (error) {
    PxMessage.error("数据格式错误，请传入合法的 JSON 格式数据");
  }
}
function downloadFileUtil(data, type, fileName = "") {
  const url = window.URL.createObjectURL(new Blob([data], { type }));
  const link = document.createElement("a");

  link.style.display = "none";
  link.href = url;
  link.setAttribute("download", fileName);
  document.body.appendChild(link);
  link.click();

  document.body.removeChild(link);
  window.URL.revokeObjectURL(url);
}

function handleFileChange(e) {
  const file = e.target.files[0];
  if (!file.type.includes("image")) {
    PxMessage.warning("只能插入图片");
    return;
  }

  const reader = new FileReader();
  reader.onload = res => {
    const fileResult = res.target.result;
    const img = new Image();
    img.onload = () => {
      const component = {
        ...commonAttr,
        id: nanoid(),
        component: "Picture",
        label: "图片",
        icon: "",
        propValue: {
          url: fileResult,
          flip: {
            horizontal: false,
            vertical: false
          }
        },
        style: {
          ...commonStyle,
          top: 0,
          left: 0,
          width: img.width,
          height: img.height
        }
      };

      // 根据画面比例修改组件样式比例 https://github.com/woai3c/visual-drag-demo/issues/91
      changeComponentSizeWithScale(component);

      addComponent({ component });

      const { recordSnapshot } = useSnapshot();
      recordSnapshot();

      // 修复重复上传同一文件，@change 不触发的问题
      $("#input").setAttribute("type", "text");
      $("#input").setAttribute("type", "file");
    };

    img.src = fileResult.toString();
  };

  reader.readAsDataURL(file);
}

// use for shortcutKey
eventBus.on("save", () => save());
eventBus.on("preview", () => preview());
eventBus.on("clearCanvas", () => clearCanvas());

// 组合拆分相关

const { areaData } = storeToRefs(useCompose());
const { compose: composeAction, decompose: decomposeAction, syncGroupStyle, batchDeleteComponent, createGruop, setAreaData } = useCompose();

function compose() {
  composeAction(componentData);
  recordSnapshot();
}
function decompose() {
  decomposeAction(curComponent);
  recordSnapshot();
}
function saveEditerData() {
  componentData.value = aceRef.value.getValue();
  isShowJsonDialog.value = false;
}
const compSelected = ref([]);
const compLabel = ref("");
function compSelectedChange() {
  const areaData = sortByOrderInA1(componentData.value, compSelected.value);
  if (areaData.length <= 1) {
    eventBus.emit("hideArea");
    return;
  }
  let top = Infinity,
    left = Infinity;
  let right = -Infinity,
    bottom = -Infinity;
  areaData.forEach(component => {
    let style = {
      left: 0,
      top: 0,
      right: 0,
      bottom: 0
    };
    // if (component.component == "Group") {
    //   component.propValue.forEach(item => {
    //     const rectInfo = $(`#component${item.id}`).getBoundingClientRect();
    //     style.left = rectInfo.left - editorX.value;
    //     style.top = rectInfo.top - editorY.value;
    //     style.right = rectInfo.right - editorX.value;
    //     style.bottom = rectInfo.bottom - editorY.value;

    //     if (style.left < left) left = style.left;
    //     if (style.top < top) top = style.top;
    //     if (style.right > right) right = style.right;
    //     if (style.bottom > bottom) bottom = style.bottom;
    //   });
    // } else {
    //   style = getComponentRotatedStyle(component.style);
    // }
    style = getComponentRotatedStyle(component.style);

    if (style.left < left) left = style.left;
    if (style.top < top) top = style.top;
    if (style.right > right) right = style.right;
    if (style.bottom > bottom) bottom = style.bottom;
  });
  setAreaData({
    style: {
      left,
      top,
      width: right - left,
      height: bottom - top
    },
    label: compLabel.value ? compLabel.value : "",
    components: areaData
  });
  // const groupComponent = {
  //   id: nanoid(),
  //   component: "Group",
  //   label: "组合",
  //   icon: "zuhe",
  //   belong: "middle",
  //   ...commonAttr,
  //   style: {
  //     ...commonStyle,
  //     left,
  //     top,
  //     width: right - left,
  //     height: bottom - top
  //   },
  //   propValue: { components: areaData, filter: "", isEmpty: false }
  // };

  // createGroupStyle(groupComponent);
  // const { addComponent, setCurComponent } = useComponent();
  // Assuming addComponent and batchDeleteComponent are actions in another store

  // addComponent({
  //   component: groupComponent
  // });

  // eventBus.emit("hideArea");

  // batchDeleteComponent(componentData, areaData);
  // setCurComponent({
  //   component: componentData.value[componentData.value.length - 1],
  //   index: componentData.value.length - 1
  // });

  // curComponent.value.style = { ...curComponent.value.style, left, top, width: right - left, height: bottom - top };
  // curComponent.value.propValue.components = areaData;
}
function mergeToCamelCase(str1, str2) {
  // 将字符串转换为小写并分割成单词数组
  let words1 = str1.toLowerCase().split(" ");
  let words2 = str2.toLowerCase().split(" ");

  // 合并两个数组，确保第一个单词的首字母小写，其余单词首字母大写
  let combinedWords = [...words1, ...words2].map((word, index) => {
    return index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1);
  });

  // 将处理后的单词数组合并成一个驼峰式字符串
  return combinedWords.join("");
}
function sortByOrderInA1(a1, a2) {
  // 创建一个映射，存储a1中元素的id和其索引的对应关系
  const idToIndexMap = new Map();
  a1.forEach((item, index) => {
    idToIndexMap.set(item.id, index);
  });

  // 使用map中的索引对a2进行排序
  return a2.sort((a, b) => {
    return idToIndexMap.get(a.id) - idToIndexMap.get(b.id);
  });
}
function getComponent(index) {
  return componentData.value[componentData.value.length - 1 - index];
}
function composeDialogClose() {
  isShowComposeDialog.value = false;
  compSelected.value = [];
  compLabel.value = "";
  eventBus.emit("isShowArea");
}
function composeDialogConfirm() {
  compSelectedChange();
  compose();
  composeDialogClose();
}
function mergeItems(arr, typeKey = "type", codeKey = "specCode") {
  // 1. 生成所有可能的匹配对
  const pairs = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      const a = arr[i][codeKey];
      const b = arr[j][codeKey];
      let len = 0;

      // 计算最长公共前缀
      while (len < a.length && len < b.length && a[len] === b[len]) {
        len++;
      }

      if (len > 0) {
        pairs.push({
          i,
          j,
          len,
          types: [arr[i][typeKey], arr[j][typeKey]],
          items: [arr[i], arr[j]]
        });
      }
    }
  }

  // 2. 按匹配长度降序排序
  pairs.sort((a, b) => b.len - a.len);

  // 3. 执行合并操作
  const result = [];
  const merged = new Set();

  pairs.forEach(({ i, j, types, items }) => {
    if (!merged.has(i) && !merged.has(j)) {
      const mergedObj = {};
      types.forEach((t, idx) => {
        mergedObj[t] = items[idx];
      });
      result.push(mergedObj);
      merged.add(i);
      merged.add(j);
    }
  });

  // 4. 处理未合并项
  arr.forEach((item, index) => {
    if (!merged.has(index)) {
      result.push({ [item[typeKey]]: item });
    }
  });

  return result;
}
</script>
<style lang="scss">
.tool-bar {
  padding: 0 15px 10px;
  overflow-x: auto;
  white-space: nowrap;
  background: #fff;
  border-bottom: 1px solid #ddd;

  .canvas-config {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 10px;
    font-size: 14px;
    color: var(--text-color);

    input {
      width: 50px;
      padding: 0 5px;
      margin-left: 4px;
      color: #606266;
      border: 1px solid var(--border-color);
      outline: none;
    }

    span {
      margin-left: 10px;
    }
  }
}

.insert {
  box-sizing: border-box;
  display: inline-block;
  padding: 9px 15px;
  margin: 0;
  margin-right: 10px;
  margin-left: 10px;
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  color: #606266;
  text-align: center;
  white-space: nowrap;
  appearance: none;
  cursor: pointer;
  background: #fff;
  border: 1px solid #dcdfe6;
  border-radius: 3px;
  outline: 0;
  transition: 0.1s;

  &:active {
    color: #409eff;
    background: #ebf5ff;
    border-color: #c5e1ff;
  }

  &:hover {
    color: #409eff;
    background: #ebf5ff;
    border-color: #c5e1ff;
  }
}
</style>
